////////////////////////////////////////////////////////////////////////////
//
//  Crytek Engine Source File.
//  Shader extension
//  Copyright (C), Crytek Studios, 2001-2004.
// -------------------------------------------------------------------------
//  File name:   ShadeLib.cfi
//  Version:     v1.00
//  Created:     19/05/2006 by Tiago Sousa
//  Compilers:   
//  Description: Common BRDF's/shading utilities library
//
// -------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////

// Common Samplers //////////////////////

DIFFUSEMAP
BUMPMAP
BUMPDIFFUSEMAP 
BUMPHEIGHTMAP
SHADOWOCCLUDEMAP
PROJECTIONMAP
PROJECTIONCUBE
GLOSSMAP 
SCENEDEPTHMAP

sampler2D diffuseMapSampler_Decal
{ 
	Texture = $Diffuse; 	
	AddressU = Border;
	AddressV = Border;
	BorderColor = {0, 0, 0, 0};	
  sRGBLookup = true; 
};

sampler2D detailMapSampler = sampler_state
{
  Texture = $Detail;
};

// Deferred Shading Samplers /////////////

SCENE_NORMALS_MAP
SCENE_NORMALS_BENT
SCENE_UVS_MAP
SCENE_DIFFUSEACC_MAP
SCENE_SPECULARACC_MAP
SCENE_HDRTARGET_ENCODED_MAP
SCENE_DECALS_ACC_MAP
SCENE_SNOW_MAP_SAMPLER

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

#if D3D11

Texture2DMS<float4>  sceneNormalsSamplerMS : register (s12)
{ 
  Texture = $SceneNormalsMapMS; 
  MinFilter = POINT; 
  MagFilter = POINT; 
  MipFilter = POINT; 
  AddressU = Clamp; 
  AddressV = Clamp; 
  Global = true;
}; 

Texture2DMS<float4>  sceneDiffuseAccSamplerMS : register (s13)
{ 
  Texture = $SceneDiffuseAccMS; 
  MinFilter = POINT; 
  MagFilter = POINT; 
  MipFilter = POINT; 
  AddressU = Clamp; 
  AddressV = Clamp; 
  Global = true;   
}; 

Texture2DMS<float4>  sceneSpecularAccSamplerMS : register (s10)
{ 
  Texture = $SceneSpecularAccMS; 
  MinFilter = POINT; 
  MagFilter = POINT; 
  MipFilter = POINT; 
  AddressU = Clamp; 
  AddressV = Clamp; 
  Global = true;   
}; 

Texture2DMS <float4>  depthMapSamplerMS = sampler_state
{ 
  Texture = $ZTargetMS; 
  MinFilter = POINT; 
  MagFilter = POINT; 
  MipFilter = POINT; 
  AddressU = Clamp;
  AddressV = Clamp;
}; 

Texture2DMS<float4>   sceneUVsSamplerMS = sampler_state
{ 
	Texture = $SceneUVsMapMS;
	MinFilter = POINT;
	MagFilter = POINT;
	MipFilter = POINT;
	AddressU = Clamp;
	AddressV = Clamp;
};

half4 GetSceneDiffuseAccMS( float2 baseTC, int NumSamples, int nCurrSample ) 
{
	int3 vPixCoord = int3( baseTC * PS_ScreenSize.xy, 0);
  return sceneDiffuseAccSamplerMS.Load(vPixCoord, nCurrSample);
}

half4 GetSceneSpecularAccMS( float2 baseTC, int NumSamples, int nCurrSample ) 
{
	int3 vPixCoord = int3( baseTC * PS_ScreenSize.xy, 0);
  return sceneSpecularAccSamplerMS.Load(vPixCoord, nCurrSample);
}

half4 GetSceneNormalsMS( float2 baseTC, int NumSamples, int nCurrSample ) 
{
	int3 vPixCoord = int3( baseTC * PS_ScreenSize.xy, 0);
  return GetWorldSpaceNormals( sceneNormalsSamplerMS.Load(vPixCoord, nCurrSample), true );
}

half4 GetSceneUVsMS( float2 baseTC, int NumSamples, int nCurrSample ) 
{
	int3 vPixCoord = int3( baseTC * PS_ScreenSize.xy, 0);
  return sceneUVsSamplerMS.Load(vPixCoord, nCurrSample);
}

half4 GetDecodedSceneDepthMS( float2 baseTC, int NumSamples, int nCurrSample ) 
{
	int3 vPixCoord = int3( baseTC * PS_ScreenSize.xy, 0);  
  return depthMapSamplerMS.Load(vPixCoord, nCurrSample).x * PS_NearFarClipDist.y; // scale back to full range
}

#endif


////////////////////////////////////////////////////////////////////////////////////////////////////

#define REFLECT_OFF    0
#define REFLECT_CUBE   1
#define REFLECT_SPHERE 2

////////////////////////////////////////////////////////////////////////////////////////////////////

float4 miscCamFront : PB_CameraFront;

//////////////////////////////// Shared fragment-shading pass structure /////////////////////////////////////

struct fragInput
{
  //===============================================================================
  // input attribute data
  float4 baseTC;
  float4 basesectorTC;
  float4 bumpTC;
  float4 terrainDetailTC;
  
  half4 vTangent;
  half4 vBinormal;
  half4 vNormal;
  half4 vView;
  
  float4 screenProj;
  float4 projTC;
  
  half4 Color;
  half4 Color1;
  float4 VisTerrainCol;
  float4 SunRefl;
  half4 Ambient;

  half4 AlphaTest;
  
  float3 DistAtten;
  float4 AvgFogVolumeContrib;
  
  float4 OutdoorAOInfo;

#if D3D11
	uint uSample;
#endif
};

struct fragPass
{
  fragInput IN;

  // Usage flags fo shared stuff  
  bool bCustomComposition;      // dont use shared final shading composition
  bool bRenormalizeNormal;
	bool bForceRenormalizeNormal;
  bool bDontUseBump;
  bool bDetailBumpMapping;
  bool bDetailBumpMappingMasking;
  bool bVertexColors;      // apply vertex color to final result
  bool bAlphaGlow;         // use diffuse texture alpha has glow
  bool bHemisphereLighting;
  bool bDontUseEmissive;
  bool bRefractionMap; 
	bool bSkinIrradiancePass; 
  bool bDisableInShadowShading;  
  bool bDisableAlphaTestD3D10; 
  bool bDeferredSpecularShading;
  bool bSkipMaterial;
  int  nReflectionMapping; // reflection mapping type (0: off, 1: cube map, 2: spherical)

  int  nMicroDetailQuality;
  half fBumpHeightScale;
  half fHeightBias;
  half fSelfShadowStrength;

  half2 vDetailBumpTilling; // detail diffuse_bump mapping tilling
	half2 vDetailBlendAmount; // detail diffuse and gloss blend scale
  half fDetailBumpScale; 	  // detail mapping scale
  
  half fLod;		 					 // lod scale: 1 max, 0 disabled

  half blendFac;          // per-pixel layer blending factor

  // shared fields
  int nQuality;           // shader quality level
  
  half3x3 mTangentToWS;  // tangent to world space transformation matrix - might be required for some vectors
  half3 vView;           // eye vector, fFogFrac
  half3 vNormal;         // normal vector
  half3 vNormalDiffuse;  // diffuse normal vector
  half3 vReflVec;        // reflection vector
          
  half3 cBumpMap;        // tangent space normal map
  half4 cDiffuseMap;     // diffuse map
  half4 cGlossMap;       // specular/gloss map
  half3 cEnvironment;    // environment map
  half4 cShadowOcclMap;  // shadow map
  half4 cSnowMap;		 // snow map

  half fNdotE;           // per pass constant NdotE
  half fGloss;           // gloss/roughness
  half fAlpha;           // opacity   
  half fAlphaTestRef;    // instanced alpha test value

  // Deferred rendering RTs (multisampling friendly - check frag_get_deferred_buffers in fraglib.cfi for example usage in your custom shader)
  half4 cNormalMapRT;
  half4 cDiffuseAccRT;
  half4 cSpecularAccRT;
  half fSceneDepthRT;
  half2 vSceneUVsRT;

  // Note: parser bug, assumes fReflectionAmount, fFresnelBias, etc, are same as tweakable parameters, therefore the _ was added
  
  half fFresnel_Bias;     // fresnel biasing
  half fFresnel_Scale;    // fresnel scale
	half fFresnel_Pow;			// fresnel power factor
  half fFresnel;					// final fresnel term
  
  half fAlphaGlow_Multiplier;   // apply multiplier to diffuse alpha glow 
  
  // Shading accumulation
  
  half3 cAmbientAcc;
  half3 cDiffuseAcc;
	half3 cBackDiffuseAcc;
  half3 cSpecularAcc;

  // Custom per pass data   
  fragPassCustom pCustom;
};
  
struct fragLightPass
{
  int nType;          // light type
  half3 cDiffuse;    // light diffuse color
  half3 cSpecular;   // light specular color
  half3 cFilter;     // light filter color
  half3 vLight;      // light vector
    
  half fNdotL;       // normal dot light
  half fFallOff;     // light attenuation  
  half fOcclShadow;  // light shadow term
  
  half3 cOut;        // light final contribution  

  // ... Custom per light data ...
  fragLightPassCustom pCustom;
}; 


//////////////////////////////// Common shading utilities ////////////////

// assumes 0 is min
half smoothstep_opt(in half maxi, in half x)
{
  x = saturate( x / maxi );
  return  x * x  * (3.0 - 2.0 * x);
}

half GlossToSpecExp255(in half gloss)
{
	return gloss * 255.h;
}

half GetFresnel(half NdotI, half bias, half power)
{
  half facing = (1.0 - NdotI);
  return saturate(bias + (1-bias)*pow(facing, power));
}

half GetAttenuation(half3 L, half fInvRadius)
{
  half3 vDist = L * fInvRadius;
  half fFallOff = saturate(1-dot(vDist, vDist));

  return fFallOff;
}

half3 ShiftVector(half3 V, half3 N, half shiftAmount)
{
  return normalize(V + shiftAmount * N);
  // 3 alu, 1 mad
}

// optimized shift vector - skips normalization - use only when vector lenght not relevant
half3 ShiftVectorOpt(half3 V, half3 N, half shiftAmount)
{
  return (V + shiftAmount * N);
  // 1 mad
}

//////////////////////////////// Common HDR encoding/decoding ////////////////////////////////

#define MAX_FLOAT						128.h

#define fHDR_EXP_BASE_1_04	1.04h
#define fHDR_EXP_BASE_1_06	1.06h
#define fHDR_EXP_OFFSET			128.h

// Using RGBK format (multiplier in alpha - filtering should work fine)
// quality: good	
half4 EncodeRGBK(in half4 Color, const half fMultiplier, bool bUsePPP = false)
{
	const half4 cScale = half4(half3(1.h, 1.h, 1.h) / fMultiplier, 1.h / 255.0);
	half fMax = saturate(dot(half4(Color.rgb, 1.h), cScale));   // 1 alu

	Color.a = ceil(fMax * 255.h) / 255.h;                       // 3 alu

	Color.xyz /= Color.a * fMultiplier;                         // 2alu

  if( bUsePPP )
  {
	//Color *= rsqrt( Color ); // for best quality

	Color.a = sqrt( Color.a ); // encode just multiplier for performance reasons
  }

  return Color;
}

void EncodeRGBKPair(inout half4 Color0, inout half4 Color1, const half fMultiplier, bool bUsePPP = false)
{
  half fMax0 = saturate(dot(Color0.rgb, 1.h / fMultiplier));    // 1 alu
  half fMax1 = saturate(dot(Color1.rgb, 1.h / fMultiplier));    // 1 alu

  Color0.a = ceil(fMax0 * 255.h) / 255.h;                       // 3 alu
  Color1.a = ceil(fMax1 * 255.h) / 255.h;                       // 3 alu

  Color0.xyz /= Color0.a * fMultiplier;                         // 2alu
  Color1.xyz /= Color1.a * fMultiplier;                         // 2alu
  
  if( bUsePPP )
  {
	  //Color0 *= rsqrt( Color0 ); // for best quality
	  //Color0 *= rsqrt( Color0 ); 

		Color0.a = sqrt( Color0.a ); // encode just multiplier for performance reasons
		Color1.a = sqrt( Color1.a );

  }
}

#ifdef CAFE
half4 DecodeRGBK(in half4 Color, const half fMultiplier, bool bUsePPP)
#else
half4 DecodeRGBK(in half4 Color, const half fMultiplier, bool bUsePPP= false)
#endif
{
  if( bUsePPP )
  {
	 //Color.rgb *= Color.rgb * (Color.a * Color.a) * fMultiplier;

	Color.rgb *= (Color.a * Color.a) * fMultiplier;
  }
  else
    Color.rgb *= Color.a * fMultiplier;

  return Color;
}

//////////////////////////////////////////////////////////////////////////////////////////////////
// These functions are used for each and every manipulation with compressed HDR buffer 
// Currently used only on PS3 because of RT readback possibility

#define SCENE_HDR_MULTIPLIER 32.h

half4 EncodeHDRBuffer( in half4 color )
{
#if PS3
	return EncodeRGBK(color, SCENE_HDR_MULTIPLIER, false);
#else
	return color;
#endif
}

void EncodeLightBufferPair( inout half4 diffuse, inout half4 specular )
{
#if PS3
  EncodeRGBKPair(diffuse, specular, SCENE_HDR_MULTIPLIER, false);
#endif
}

half4 DecodeHDRBuffer( in half4 rgbk )
{
#if PS3
	return DecodeRGBK(rgbk, SCENE_HDR_MULTIPLIER, false);
#else
	return rgbk;
#endif
}

half4 EncodeLightBuffer( in half4 color )
{
#if PS3
	return EncodeRGBK(color, SCENE_HDR_MULTIPLIER, false);
#else
	return color;
#endif
}

half4 Decode7E3F(in half4 color)
{
	// Reference	
	//vColor.rgb *= 8.0f;  // Shift performed in resolve and tex bias
	//float3 e = floor( vColor.rgb );
	//float3 m = frac( vColor.rgb );
	//vColor.rgb  = (e == 0.0f) ? 2*m/8 : (1+m)/8 * pow(2,e);  

	float3 me = color.xyz;
	float3 e  = floor(me);
	float3 m  = frac(me);	
		
	color.xyz = (e == 0.0f) ? 2.0f * m : (1.0f+m) * exp2(e);
	color.xyz *= 0.125f;

	return color;
}

half4 DecodeLightBuffer( in half4 color )
{
#if PS3
	return DecodeRGBK(color, SCENE_HDR_MULTIPLIER, false);
#else
#if XENON
	color = Decode7E3F(color);
#endif
	return color;
#endif
}

//////////////////////////////// Color spaces conversion ////////////////////////////////

half3 RGBtoXYZ( in half3 cRGB )
{
	const half3x3 RGB_XYZ = 
	{
		half3(0.4125, 0.3576, 0.1805), 
		half3(0.2126, 0.7152, 0.0722),
		half3(0.0193, 0.1192, 0.9505)
	};

	half3 cXYZ = mul(RGB_XYZ, cRGB); 

	// XYZ into to Yxy
	half3 Yxy; 
	Yxy.r = cXYZ.g;  
	Yxy.gb = cXYZ.rg / ( dot(1.h, cXYZ) );

	return Yxy;
}

half3 XYZtoRGB( in half3 Yxy )
{
	const half3x3 XYZ_RGB = 
	{
		half3(3.2410, -1.5374, -0.4986), 
		half3(-0.9692, 1.8760, 0.0416),
		half3(0.0556, -0.2040, 1.0570)
	};

	half3 cXYZ = 0;

	// Put back into XYZ
	cXYZ.rgb = half3( Yxy.g * (Yxy.r / Yxy. b), 
										Yxy.r,
									 (1 - Yxy.g - Yxy.b) * (Yxy.r / Yxy.b) );

	// XYZ into RGB
	half3 cRGB = mul(XYZ_RGB, cXYZ); 

	return cRGB;
}

//////////////////////////////// Common Brdfs ////////////////////////////////

//////////////////////////////// Phong model /////////////////////////////////////
// - Phong model has good properties for plastic and some metallic surfaces. 
// - Good for general use. Very cheap.

#define ONE_OVER_PI 0.31831h
#define ONE_OVER_TWO_PI 0.159155h

// Optimized phong, use if mirrowed reflection vector pre-computed
half Phong(half3 R, half3 L, half Exp)
{	
	half fNormFactor = Exp * ONE_OVER_TWO_PI + ONE_OVER_TWO_PI;		// 1 ALU
  return fNormFactor *  pow(saturate(dot(L, R)), Exp);					// 4 ALU
	// 5 ALU
}

half Phong(half3 N, half3 V, half3 L, half Exp)
{
  half3 R = reflect(-V, N);	// 3 ALU
  return Phong(R, L, Exp);	// 5 ALU
  // 8 ALU
}

//////////////////////////////// Blinn BRDF model /////////////////////////////////////
// - Blinn model has good properties for plastic and some metallic surfaces. 
// - Good for general use. Very cheap.
// *NOTE* We should also multiply by the clamped N.L factor. However this is 
// delegated to the shader part for performance reasons

half BlinnBRDF(half3 N, half3 V, half3 L, half Gloss)
{
  half3 H = normalize(V + L);
	
	// Compute perceptually linear exponent in range 2-2048
	float power = exp2( 10.h * Gloss + 1.h );
	
	float fNormFactor = power * (1.0/8.0) + (2.0/8.0);
	return fNormFactor * pow( saturate( dot( N, H ) ), power );
}


//////////////////////////////// Ward model, anisotropic /////////////////////////
// - Ward anisotropic model has good properties for generic anisotropic highlights 
// (can have radial or disk shape)
// - Pure math versions are expensive

half WardAnisotropic_opt(half3 T, half3 B, half3 N, half3 V, half3 L, half NdotL, half NdotE, half2 vSurfaceRoughness, half fGloss)
{
  // opt: averaging vectors instead of normalization
  //half3 H =  (V + L)*  0.5;                                        // 1 alu
  half3 H =  normalize(V + L);                                        // 1 alu


  half NdotH = dot( N, H );                                                    // 1 alu

  half2 HdotT_B = ( half2( dot( H, B ), dot( H, T ) ) )* vSurfaceRoughness.xy;                 // 3 alu
  HdotT_B *= HdotT_B;                                                                             // 1 alu

  // using sat instead, since it's free, but compensating by brighting specular
  half specK = saturate( rsqrt( saturate( NdotL * NdotE ) ) );                                 // 2 alu

  half Exp = GlossToSpecExp255(fGloss);
	
	// opt: merged exponential with power factor
  //specK *= NdotL * exp( Exp* (-2.0 * (HdotT_B.x + HdotT_B.y) )/(1.001 + NdotH) );  // 6 alu
  specK *= NdotL * exp( dot(HdotT_B.xy, -2.0h * Exp/(1.001h + NdotH)) );  // 6 alu

  return specK;

  // 14 alu  
}

//////////////////////////////// Kajiya-Kay model, anisotropic /////////////////////////
// - Kajiya-Kay anisotropic model has Good properties for radial shape anisotropic 
// highlights (good for hair)
// - Very cheap

half KajiyaKayAnisotropic(half3 T, half3 V, half3 L, half Exp)
{
  half3 H = normalize( V + L );																// 4 alu
  half TdotH = dot( T, H );																		// 1 dot
  
  half fSpec = sqrt( max( 1.0 - TdotH * TdotH, 0.01) );										// 1 mad, 1 sqrt
  
  return pow( fSpec, Exp );																	  	// 3 alu
  
  // aprox 10 ALU
}

// A bit cheaper version, half vector already pre-computed
half KajiyaKayAnisotropic(half3 T, half3 H, half Exp)
{	
  half TdotH = dot( T, H );																  // 1 dot	
  half fSpec = sqrt( max( 1.0 - TdotH * TdotH, 0.01) );							    // 1 mad, 1 sqrt
  
  return pow( fSpec, Exp );																		// 3 alu
  
  // aprox 6 ALU		
}

//////////////////////////////// Vegetation shading ////////////////

// Common vegetation shading

half3 LeafShadingBack(half3 vEye, half3 vLight, half3 vNormal, half3 cDiffBackK, half backViewDep)
{            
  half EdotL=saturate(dot(vEye.xyz, -vLight.xyz));          
  
  // Tweaked NdotL wrapping - Artists request
  half fLdotNBack=saturate(dot(vNormal.xyz, vLight.xyz)*0.6+0.4);

  half powEdotL = EdotL*EdotL;
  powEdotL *= powEdotL;
    
  half3 vBackShading = saturate(powEdotL*backViewDep + (1.0-backViewDep) * fLdotNBack);    

  return vBackShading * cDiffBackK.xyz;
}   

void LeafShadingFront(half3 vEye, half3 vLight, half3 vNormal, half3 cDifK, half3 cSpecK, inout half3 outDif, inout half3 outSpec, half fGloss)
{                                                                                                                                  
  half fLdotNFront=dot(vNormal.xyz, vLight.xyz);      
  // Compute front diffuse term  
#if %GRASS   
  outDif=max(fLdotNFront, 0.5)*cDifK.xyz; 
#else
  outDif=saturate(fLdotNFront)*cDifK.xyz;
#endif
  // compute specular if necessary 
#if !%GRASS
  outSpec = BlinnBRDF(vNormal, vEye, vLight, fGloss) * saturate(fLdotNFront) * cSpecK.xyz;  
#endif
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Parallel view opacity falloff (for close to parallel leaf polys)

float ParallelOpacityFalloff(float3 vWorldPos, float3 vNormal, float fCapOpacityFalloff, float fAlphaTest)
{
	half3 inverseViewVec = normalize(vWorldPos.xyz); // we use the abs of the dot on next line, so direction of view vec doesn't matter
	half parallelViewOpacity = abs(dot(inverseViewVec, vNormal));

	// cos(alpha ramp start angle) = 0.05
	// cos(alpha ramp end angle) = 0.3
	const half a = 1.0h / (0.3h - 0.05h);
	const half b = -0.05h / (0.3h - 0.05h);

	half x = parallelViewOpacity;

	x = saturate(x*a + b); // remap alpha ramp range to [0..1]
	//x *= (2.0h - x); // eval 2nd order polynomial

	parallelViewOpacity = max(x, fCapOpacityFalloff);

	return 1.0f + parallelViewOpacity * (fAlphaTest - 1.0f);
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Layer blending

#ifdef CAFE
half GetLayerBlendingValue(in sampler2D blendMapSampler, in float2 uv, in float lod, in half vtxAlpha, in half blendFactor, in half blendFalloff, bool bTerrainLayer)
#else
half GetLayerBlendingValue(in sampler2D blendMapSampler, in float2 uv, in float lod, in half vtxAlpha, in half blendFactor, in half blendFalloff, bool bTerrainLayer = false)
#endif
{
	#if !VS_TESSELLATION // TODO: find way to compute lod without gradients as they cause pixel artifacts at fin / shell transition
		half4 blendMap = GetTexture2D(blendMapSampler, uv);
	#else
		half4 blendMap = tex2Dlod(blendMapSampler, float4(uv, 0, lod));
	#endif

	// Terrain layers are a particular case of blend layer, where blendmap is the heightmap and blending
	// is only valid on transition from full opaque (vtx alpha = 1) to fully transparent (vtx alpha = 0)	
	if( bTerrainLayer )
		blendMap = saturate(saturate(vtxAlpha*2-1) + blendMap.w); // 2 inst

	half blendFac = vtxAlpha * blendMap.r * (1 + blendFactor);
	blendFac = saturate(pow(blendFac, blendFalloff));
	return blendFac;
}

void ComputeSnowBlending(inout half3 cDiffuse, in half4 cSnowMap, in half3 cDiffuseMap)
{
  // Static branch instead of shader flag (keep permutation count down).
  if(SnowVolumeParams[0].x)
  {    
	cSnowMap.z = saturate((cSnowMap.z >= 1) ? cSnowMap.z-1.h : cSnowMap.z);
	cSnowMap.x += cSnowMap.z * saturate( dot(cDiffuseMap.xyz, 1) * 1.5f );
	cDiffuse = lerp(cSnowMap.y, cDiffuse + cSnowMap.x, cSnowMap.w );
  }
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// POM & OBM tex coord gen

float2 ParallaxOcclusionMap(in float2 baseTC, in float lod, in float3 viewDirNrm, in int numSteps, in float displacement, in float bias, in float blendLayerFactor, in float blendLayer2Tiling)
{
	float step =  1.0 / numSteps;
	float bumpScale = displacement;

	float2 delta = float2(viewDirNrm.x, viewDirNrm.y) * bumpScale / (-viewDirNrm.z * numSteps); // / max(-viewDirNrm.z * numSteps, 0.1)

	baseTC -= (1.0 - bias) * numSteps * delta;

	float4 NB0 = tex2D(bumpHeightMapSampler, baseTC);
#if %BLENDLAYER
	NB0 += blendLayerFactor * (tex2D(HeightMap2Sampler, baseTC.xy * blendLayer2Tiling) - NB0);
#endif

	float height = 1 - step;
	float4 offset = float4(baseTC + delta, 0, lod);
	float4 NB1 = tex2D(bumpHeightMapSampler, offset);
#if %BLENDLAYER
	NB1 += blendLayerFactor * (tex2D(HeightMap2Sampler, offset.xy * blendLayer2Tiling) - NB1);
#endif

	for (int i=0; i<numSteps; i++)
	{
#ifndef XENON
		[flatten]
#endif
		if (NB1.w >= height)
			break;

		NB0 = NB1;

		height -= step;
		offset.xy += delta;

		NB1 = tex2Dlod(bumpHeightMapSampler, offset);
#if %BLENDLAYER
		NB1 += blendLayerFactor * (tex2Dlod(HeightMap2Sampler, float4(offset.xy * blendLayer2Tiling, offset.zw)) - NB1);
#endif
	}

	float4 offsetBest = offset;
	float error = 1.0;

	float t1 = height;
	float t0 = t1 + step;

	float delta1 = t1 - NB1.w;
	float delta0 = t0 - NB0.w;

	float4 intersect = float4(delta * numSteps, delta * numSteps + baseTC);

	for (int i=0; i<10; i++)
	{
#ifndef XENON
		[flatten]
#endif
		if (abs(error) <= 0.01)
			break;

		float denom = delta1 - delta0;
		float t = (t0 * delta1 - t1 * delta0) / denom;
		offsetBest.xy = -t * intersect.xy + intersect.zw;

		float4 NB = tex2Dlod(bumpHeightMapSampler, offsetBest);
#if %BLENDLAYER
		NB += blendLayerFactor * (tex2Dlod(HeightMap2Sampler, float4(offsetBest.xy * blendLayer2Tiling, offsetBest.zw)) - NB);
#endif

		error = t - NB.w;
		if (error < 0)
		{
			delta1 = error;
			t1 = t;
		}
		else
		{
			delta0 = error;
			t0 = t;
		}
	}

	return offsetBest.xy;
}

float2 OffsetMap(in float2 baseTC, in half3 viewDirNrm, in int numSteps, in half displacement, in half bias, in half blendLayerFactor, in float blendLayer2Tiling)
{
/*
	half height = EXPAND(tex2D(bumpHeightMapSampler, baseTC.xy).a) * displacement;
	half slope = GetNormalMap(bumpMapSampler, baseTC.xy).z;

	return baseTC.xy + viewDirNrm.xy * slope * height;
*/

	half offset = -bias * displacement;
	float3 newCoords = float3(baseTC, 0);
	for (int i=0; i<numSteps; ++i)
	{
		half nz = GetNormalMap(bumpMapSampler, newCoords.xy).z;
		half h = tex2D(bumpHeightMapSampler, newCoords.xy).a;

#if %BLENDLAYER
		const float2 newCoords2 = newCoords.xy * blendLayer2Tiling;
		nz += blendLayerFactor * (GetNormalMap(BumpMap2Sampler, newCoords2.xy).z - nz);
		h += blendLayerFactor * (tex2D(HeightMap2Sampler, newCoords2.xy).a - h);
#endif

		half height = h * displacement + offset;
		newCoords += (height - newCoords.z) * nz * viewDirNrm;
	}
	return newCoords.xy;
}

#define MICRO_DETAIL_QUALITY_DEF 0
#define MICRO_DETAIL_QUALITY_OBM 1
#define MICRO_DETAIL_QUALITY_POM 2

void GetMicroDetailParams(out int mdQuality, out half mdDisplacement, out half mdHeightBias, out half mdSelfShadowStrength)
{
	mdQuality = MICRO_DETAIL_QUALITY_DEF;
	mdDisplacement = 0.0h;
	mdHeightBias = 1.0h;
	mdSelfShadowStrength = 0.0h;

#if %OFFSETBUMPMAPPING || %PARALLAX_OCCLUSION_MAPPING
	int shQuality = GetShaderQuality();
	#if D3D9 || D3D10
		#if %OFFSETBUMPMAPPING && %PARALLAX_OCCLUSION_MAPPING
			if(shQuality > QUALITY_MEDIUM)
			{
				mdQuality = MICRO_DETAIL_QUALITY_POM;
				mdDisplacement = PomDisplacement;
			}
			else if (shQuality == QUALITY_MEDIUM)
			{
				mdQuality = MICRO_DETAIL_QUALITY_OBM;
				mdDisplacement = ObmDisplacement;
			}
		#elif %PARALLAX_OCCLUSION_MAPPING
			if (shQuality > QUALITY_MEDIUM)
			{
				mdQuality = MICRO_DETAIL_QUALITY_POM;
				mdDisplacement = PomDisplacement;
			}
		#else
			if (shQuality > QUALITY_LOW)
			{
				mdQuality = MICRO_DETAIL_QUALITY_OBM;
				mdDisplacement = ObmDisplacement;
			}
		#endif
	#else
		#if %OFFSETBUMPMAPPING
			mdQuality = MICRO_DETAIL_QUALITY_OBM;
			mdDisplacement = ObmDisplacement;
		#endif
	#endif
	mdHeightBias = HeightBias;
	mdSelfShadowStrength = SelfShadowStrength;
#endif
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// EnvMap Samplers
////////////////////////////////////////////////////////////////////////////////////////////////////

half4 GetEnvironmentCMap(samplerCUBE envMap, in half3 envTC, in half fGloss)
{
	const half numCMapMips = 6.0;  // TODO: Use real cubemap size
	
	half fGlossinessLod = numCMapMips - fGloss * numCMapMips;
#ifdef CAFE
  half4 envColor = DecodeRGBK(texCUBElod( envMap, half4(envTC, fGlossinessLod) ), HDR_FAKE_MAXOVERBRIGHT, false);
#else
  half4 envColor = DecodeRGBK(texCUBElod( envMap, half4(envTC, fGlossinessLod) ), HDR_FAKE_MAXOVERBRIGHT);
#endif
    
  return envColor;
}

half4 GetEnvironment2DMap(sampler2D envMap, in half2 envTC)
{
  half4 envColor = tex2D(envMap, envTC.xy);

	return envColor;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// HDR output

void HDRFogOutput( out pixout OUT, half4 Color, half fDepth, half3 FogColor, half fFogFactor )
{
  Color.xyz = lerp(FogColor.xyz, Color.xyz, fFogFactor);
  OUT.Color = Color;

#if %_RT_HDR_ENCODE  
  OUT.Color = EncodeRGBK(OUT.Color, SCENE_HDR_MULTIPLIER);
#endif

#if !%_RT_HDR_MODE
  // LDR mode, compress to LDR and apply gamma correction.
  OUT.Color.xyz = 1.0f - exp(-OUT.Color.xyz);
  OUT.Color.xyz = pow(OUT.Color.xyz, 1.0/2.2);
#endif
}

void HDROutput( out pixout OUT, half4 Color, half fDepth)
{
// Workaround for CAFE
#ifdef CAFE
  //Color.xyz = clamp(Color.xyz, float3(0,0,0), float3(1,1,1));
#endif

  OUT.Color = Color;

#if %_RT_HDR_ENCODE  
  OUT.Color = EncodeRGBK(OUT.Color, SCENE_HDR_MULTIPLIER);
#endif

#if !%_RT_HDR_MODE
  // LDR mode, compress to LDR and apply gamma correction.
  OUT.Color.xyz = 1.0f - exp(-OUT.Color.xyz);
  OUT.Color.xyz = pow(OUT.Color.xyz, 1.0/2.2);
#endif
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Depth/Fog output

#include "VolumetricFog.cfi"

// NOTE: float[X] was used in favor of half[X] as precision is curcial and half's is not sufficient!
half4 EncodeSceneDepth( float depth )
{
  return half4(depth,depth,depth,depth);
} 

// This function encodes a depth value so it can be stored in an ARGB8 rendertarget and sets a specific alpha value
float4 EncodeSceneDepthWithAlpha( float depth, half alpha, half alphaTestRef, float3 WorldPos)
{
  float4 ret = EncodeSceneDepth( depth );

	// on consoles we always use alpha test 
#if !XENON && !PS3
  clip(alpha - alphaTestRef); //need to perform texkill in case of r16g16`
#endif

	ret.a = alpha;

  return ret;
}

// This function encodes a depth value so it can be stored in an ARGB8 rendertarget and sets a specific alpha value
float4 EncodeSceneDepthNoAlpha( float depth, float value, float3 WorldPos)
{
  float4 ret = EncodeSceneDepth( depth );
  //ret.g = value;

  return ret;
}

// This function decodes a depth value coming from an ARGB8 rendertarget/texture 
// NOTE: 1) float[X] was used in favor of half[X] as precision is curcial and half's is not sufficient!
//			 2) smpDepth should be set up as follows...
// 
float DecodeSceneDepth( sampler2D smpDepth, float4 homogeneousPositionTexProj )
{
 	float depthNormalized = GetLinearDepth_ProjTC( smpDepth, homogeneousPositionTexProj );

	// scale back to full range
	return depthNormalized * PS_NearFarClipDist.y;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Screen space self-shadowing approximation

half ScreenSpaceSelfShadow(sampler2D smpDepth, float4x4 mViewProjection, float4 tcProj, float3 vPosWS, half3 vLight, half fSoften ) 
{
	// todo: 
	//	- jittering / try out getting away with less samples
	//	- mask samples based on distance, should only self-shadow nearby surface

	half fSign = sign(dot( vLight, miscCamFront ) );

	float3 vLightPos = vPosWS - fSign * vLight.xyz;
	half4 vLightProj = mul( mViewProjection, float4(vLightPos, 1) );
	vLightProj.x = ( vLightProj.x + vLightProj.w)*0.5f / vLightProj.w;
	vLightProj.y = (-vLightProj.y + vLightProj.w)*0.5f / vLightProj.w;		

	vLightProj.xy = vLightProj.xy - tcProj.xy;
	vLightProj.xy *= fSign;

	float2 lightDelta = -vLightProj * PS_ScreenSize.zw*100;
	  
  float h0 = tcProj.w;//GetLinearDepthScaled(smpDepth, tcProj).x;
  float h = h0;

	half hKernelDepthScale = (1 / h0 );
	//lightDelta *= hKernelDepthScale;

  h = min(65535, GetLinearDepthScaled(smpDepth, tcProj + 1.000 * lightDelta).x); 
  h = min(h, GetLinearDepthScaled(smpDepth, tcProj + 0.875 * lightDelta).x); 
  h = min(h, GetLinearDepthScaled(smpDepth, tcProj + 0.750 * lightDelta).x); //
  h = min(h, GetLinearDepthScaled(smpDepth, tcProj + 0.625 * lightDelta).x);
  h = min(h, GetLinearDepthScaled(smpDepth, tcProj + 0.500 * lightDelta).x); //
  h = min(h, GetLinearDepthScaled(smpDepth, tcProj + 0.375 * lightDelta).x);
  h = min(h, GetLinearDepthScaled(smpDepth, tcProj + 0.250 * lightDelta).x); //
  h = min(h, GetLinearDepthScaled(smpDepth, tcProj + 0.125 * lightDelta).x);

	half fSelfShadow = 1 - saturate((h0 - h ) * fSoften);
  return fSelfShadow;
}
